package com.vr.cloudpicture.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vr.cloudpicture.common.PageResult;
import com.vr.cloudpicture.enums.SpaceLevelEnum;
import com.vr.cloudpicture.enums.SpaceRoleEnum;
import com.vr.cloudpicture.enums.SpaceTypeEnum;
import com.vr.cloudpicture.enums.UserRoleEnum;
import com.vr.cloudpicture.exception.BusinessException;
import com.vr.cloudpicture.exception.ErrorCode;
import com.vr.cloudpicture.exception.ThrowUtils;
import com.vr.cloudpicture.manager.CosManager;
import com.vr.cloudpicture.manager.sharding.DynamicShardingManager;
import com.vr.cloudpicture.mapper.SpaceMapper;
import com.vr.cloudpicture.model.dto.DeleteRequest;
import com.vr.cloudpicture.model.dto.space.SpaceAddRequest;
import com.vr.cloudpicture.model.dto.space.SpaceEditRequest;
import com.vr.cloudpicture.model.dto.space.SpaceQueryRequest;
import com.vr.cloudpicture.model.dto.space.SpaceUpdateRequest;
import com.vr.cloudpicture.model.dto.user.UserLogin;
import com.vr.cloudpicture.model.entity.Space;
import com.vr.cloudpicture.model.entity.SpaceUser;
import com.vr.cloudpicture.model.vo.SpaceVO;
import com.vr.cloudpicture.service.ISpaceService;
import com.vr.cloudpicture.service.ISpaceUserService;
import com.vr.cloudpicture.service.IUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author hzh
 * @description 针对表【space(空间)】的数据库操作Service实现
 * @createDate 2025-05-18 20:21:12
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SpaceServiceImpl extends ServiceImpl<SpaceMapper, Space> implements ISpaceService {

    private final SpaceMapper spaceMapper;
    private final TransactionTemplate transactionTemplate;
    private final RedissonClient redissonClient;
    private final CosManager cosManager;
    private final IUserService userService;
    @Resource
    @Lazy
    private  ISpaceUserService spaceUserService;
    @Resource
    @Lazy
    private DynamicShardingManager dynamicShardingManager;

    private static final String UPLOAD_SPACE_PATH_PREFIX_FORMAT = "space/%s";

    @Override
    public Boolean updateSpace(SpaceUpdateRequest spaceUpdateRequest, UserLogin loginUser) {
        // 检验参数
        if (spaceUpdateRequest == null || spaceUpdateRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Space space = BeanUtil.toBean(spaceUpdateRequest, Space.class);
        fillSpaceBySpaceLevel(space);
        validSpace(space, false);
        boolean exists = spaceMapper.exists(new LambdaQueryWrapper<Space>().eq(Space::getId, space.getId()));
        ThrowUtils.throwIf(!exists, ErrorCode.NOT_FOUND_ERROR);

        return spaceMapper.updateById(space) > 0;
    }

    @Override
    public Long addSpace(SpaceAddRequest spaceAddRequest, UserLogin loginUser) {
        // 一个用户只能创建一个空间，但是可能会出现创建多个空间的情况，此时将userId作为唯一索引(应为用的是逻辑删除，所以不行)或者可以用事务加锁方式
        Space space = BeanUtil.toBean(spaceAddRequest, Space.class);
        if (StrUtil.isBlank(space.getSpaceName())) {
            space.setSpaceName("默认空间");
        }
        if (spaceAddRequest.getSpaceLevel() == null) {
            // 默认空间等级是普通版
            space.setSpaceLevel(SpaceLevelEnum.COMMON.getValue());
        }
        if (spaceAddRequest.getSpaceType() == null) {
            // 默认创建私有空间
            space.setSpaceLevel(SpaceTypeEnum.PRIVATE.getValue());
        }
        // 填充数据
        fillSpaceBySpaceLevel(space);
        // 数据校验
        validSpace(space, true);
        Long userId = loginUser.getId();
        space.setUserId(userId);
        // 获取锁,intern()方法返回的是字符串常量池中的对象,不存在是会创建，保证相同的userId是同一个对象，但不会及时释放
        // String lock = String.valueOf(userId).intern();
        ConcurrentHashMap<String, Object> lockMap = new ConcurrentHashMap<>();
        // 保证同一个key是同一个对象
        // Object lock = lockMap.computeIfAbsent(userId.toString(), (k) -> new Object());
        Long spaceId = null;
        // 使用redisson分布式锁
        RLock lock = redissonClient.getLock(userId.toString());
            try {
                lock.lock();
                // 编程式事务,将事务控制在锁内
                spaceId = transactionTemplate.execute(status -> {
                    // 自动开启事务
                    boolean exists = spaceMapper.exists(new LambdaQueryWrapper<Space>()
                            .eq(Space::getUserId, userId)
                            .eq(Space::getSpaceType,space.getSpaceType()));
                    ThrowUtils.throwIf(exists, ErrorCode.OPERATION_ERROR, "每个用户每类空间只能创建一个");
                    int insert = spaceMapper.insert(space);
                    ThrowUtils.throwIf(insert <= 0, ErrorCode.OPERATION_ERROR);
                    if (SpaceTypeEnum.TEAM.getValue().equals(space.getSpaceType())) {
                        // 当创建团队空间成功时，默认创建者是第一个成员并且权限是空间管理员
                        SpaceUser spaceUser = SpaceUser.builder()
                                .userId(userId)
                                .spaceId(space.getId())
                                .spaceRole(SpaceRoleEnum.ADMIN.getValue())
                                .build();
                        boolean result = spaceUserService.save(spaceUser);
                        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR,"创建团队成员记录失败");
                    }
                    // 动态分表
                    // dynamicShardingManager.createSpacePictureTable(space);
                    return space.getId();
                    // 正常结束，自动提交事务
                });
            } finally {
                // 释放锁防止内存泄露
                // lockMap.remove(userId.toString());
                lock.unlock();
            }
            return Optional.ofNullable(spaceId).orElse(-1L);
    }

    @Override
    public void checkSpace(Space space) {
        ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR,"空间不存在");
        // 校验额度
        ThrowUtils.throwIf(space.getTotalCount()>=space.getMaxCount(), ErrorCode.OPERATION_ERROR,"空间条数不足");
        ThrowUtils.throwIf(space.getTotalSize()>=space.getMaxSize(), ErrorCode.OPERATION_ERROR,"空间容量不足");
    }

    @Override
    public Boolean deleteSpace(DeleteRequest deleteRequest, UserLogin loginUser) {
        // 空间的话只有自己能删
        ThrowUtils.throwIf(deleteRequest==null, ErrorCode.PARAMS_ERROR);
        long spaceId = deleteRequest.getId();
        Space space = spaceMapper.selectById(spaceId);
        validSpace(space,false);
        ThrowUtils.throwIf(!space.getUserId().equals(loginUser.getId()), ErrorCode.NOT_AUTH_ERROR);
        boolean delete = spaceMapper.deleteById(spaceId) > 0;
        if (delete) {
            // 异步批量删除空间下的所有图片,也就是删除目录
            String directoryKey=String.format(UPLOAD_SPACE_PATH_PREFIX_FORMAT,spaceId);
            CompletableFuture.runAsync(() -> {
                cosManager.deleteDirectory(directoryKey);
            });
        }
        return true;
    }

    @Override
    public Boolean editSpace(SpaceEditRequest spaceEditRequest, UserLogin loginUser) {
        if (spaceEditRequest == null || spaceEditRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Space space = spaceMapper.selectById(spaceEditRequest.getId());
        validSpace(space, false);
        // 本人或管理才能编辑空间
        if (!loginUser.getId().equals(space.getUserId()) && !loginUser.getUserRole().equals(UserRoleEnum.ADMIN.getValue())) {
            throw new BusinessException(ErrorCode.NOT_AUTH_ERROR, "没有权限");
        }
        return spaceMapper.updateById(space)>0;
    }

    @Override
    public PageResult<Space> listSpaceByPage(SpaceQueryRequest spaceQueryRequest) {
        ThrowUtils.throwIf(spaceQueryRequest == null, ErrorCode.PARAMS_ERROR);
        // 开启分页
        Page<Space> page = Page.of(spaceQueryRequest.getPageNum(), spaceQueryRequest.getPageSize());
        LambdaQueryWrapper<Space> pageQueryWrapper = this.buildPageQueryWrapper(spaceQueryRequest);
        Page<Space> pageResult = this.page(page, pageQueryWrapper);
        return new PageResult<Space>()
                .setPageNum(pageResult.getCurrent())
                .setPageSize(pageResult.getSize())
                .setTotal(pageResult.getTotal())
                .setRecords(pageResult.getRecords());
    }

    @Override
    public PageResult<SpaceVO> listSpaceVOByPage(SpaceQueryRequest spaceQueryRequest, UserLogin loginUser) {
        ThrowUtils.throwIf(spaceQueryRequest==null,ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(!spaceQueryRequest.getUserId().equals(loginUser.getId()),ErrorCode.NOT_AUTH_ERROR);
        // 开启分页
        Page<Space> page = Page.of(spaceQueryRequest.getPageNum(), spaceQueryRequest.getPageSize());
        LambdaQueryWrapper<Space> pageQueryWrapper = this.buildPageQueryWrapper(spaceQueryRequest);
        Page<Space> pageResult = this.page(page, pageQueryWrapper);
        List<SpaceVO> spaceVOList = BeanUtil.copyToList(pageResult.getRecords(), SpaceVO.class);
        return new PageResult<SpaceVO>()
                .setPageNum(pageResult.getCurrent())
                .setPageSize(pageResult.getSize())
                .setTotal(pageResult.getTotal())
                .setRecords(spaceVOList);
    }

    @Override
    public void checkSpaceAuth(Space space, UserLogin loginUser) {
        ThrowUtils.throwIf(space==null,ErrorCode.NOT_FOUND_ERROR);
        boolean condition=!space.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser);
        ThrowUtils.throwIf(condition,ErrorCode.NOT_AUTH_ERROR);
    }

    private LambdaQueryWrapper<Space> buildPageQueryWrapper(SpaceQueryRequest spaceQueryRequest) {
        return new LambdaQueryWrapper<Space>()
                .like(StrUtil.isNotBlank(spaceQueryRequest.getSpaceName()), Space::getSpaceName, spaceQueryRequest.getSpaceName())
                .eq(ObjUtil.isNotEmpty(spaceQueryRequest.getUserId()), Space::getUserId, spaceQueryRequest.getUserId())
                .eq(ObjUtil.isNotEmpty(spaceQueryRequest.getSpaceLevel()), Space::getSpaceLevel, spaceQueryRequest.getSpaceLevel())
                .eq(ObjUtil.isNotEmpty(spaceQueryRequest.getSpaceType()), Space::getSpaceType, spaceQueryRequest.getSpaceType())
                .orderBy(true,true,Space::getCreateTime,Space::getUpdateTime,Space::getEditTime);
    }

    @Override
    public void fillSpaceBySpaceLevel(Space space) {
        // 根据空间级别自动填充
        SpaceLevelEnum spaceLevelEnum = SpaceLevelEnum.getEnumByValue(space.getSpaceLevel());
        if (spaceLevelEnum != null) {
            // 没有设置最大容量和最大数量，则按照默认来
            if (space.getMaxSize() == null) {
                space.setMaxSize(spaceLevelEnum.getMaxSize());
            }
            if (space.getMaxCount() == null) {
                space.setMaxCount(spaceLevelEnum.getMaxCount());
            }
        }
    }

    @Override
    public void validSpace(Space space, boolean add) {
        if (space == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        String spaceName = space.getSpaceName();
        Integer spaceLevel = space.getSpaceLevel();
        Integer spaceType = space.getSpaceType();
        SpaceLevelEnum spaceLevelEnum = SpaceLevelEnum.getEnumByValue(spaceLevel);
        SpaceTypeEnum spaceTypeEnum = SpaceTypeEnum.getEnumByValue(spaceType);
        // 创建空间时
        if (add) {
            ThrowUtils.throwIf(StrUtil.isBlank(spaceName), ErrorCode.PARAMS_ERROR, "空间名称不能为空");
            ThrowUtils.throwIf(spaceLevel == null, ErrorCode.PARAMS_ERROR, "空间等级不能为空");
            ThrowUtils.throwIf(spaceType == null, ErrorCode.PARAMS_ERROR, "空间类型不能为空");
        }
        // 更新和创建都需要验证的
        ThrowUtils.throwIf(spaceLevel != null && spaceLevelEnum == null, ErrorCode.PARAMS_ERROR, "空间等级不合法");
        ThrowUtils.throwIf(spaceType!=null && spaceTypeEnum==null,  ErrorCode.PARAMS_ERROR, "空间类型不合法");
        ThrowUtils.throwIf(StrUtil.isNotBlank(spaceName) && spaceName.length() > 30, ErrorCode.PARAMS_ERROR, "空间名称过长，不能超过30个字符");

    }
}




